﻿namespace ImageProcessing
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.Drawing.Imaging;

    /// <summary>
    /// Adding Type 
    /// </summary>
    public enum AddingType
    {
        /// <summary>
        /// Black Rows and Colomns
        /// </summary>
        Black,

        /// <summary>
        /// Same neighbors Rows and Colomns
        /// </summary>
        Same
    }

    /// <summary>
    /// Cut Type 
    /// </summary>
    public enum PostProcessing
    {
        /// <summary>
        /// Cut Off dots
        /// </summary>
        CuttOff,

        /// <summary>
        /// Normalize Image
        /// </summary>
        Normalization,

        /// <summary>
        /// Do Nothing 
        /// </summary>
        None
    }

    /// <summary>
    /// Neighborhood Operations Class
    /// </summary>
    public class NeighborhoodOperations
    {
        /// <summary>
        /// Convolutions the specified image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="mask">The mask.</param>
        /// <param name="postProcessing">The post processing.</param>
        /// <param name="type">The type.</param>
        /// <returns>new image </returns>
        public static ImageStructure[,] Convolution(ImageStructure[,] image, double[,] mask, PostProcessing postProcessing, AddingType type)
        {
            int height = image.GetLength(0);
            int width = image.GetLength(1);
            int pProcessing = postProcessing == PostProcessing.Normalization ? 1 : 0;
            int k = mask.GetLength(0);
            double[] mask2 = { 0 };
            ImageStructure[,] newImage = new ImageStructure[height, width];
            Bitmap b1, br;
            b1 = OpenImage.ConvertStructureToBitmap(image, width, height);
            br = new Bitmap(width, height, PixelFormat.Format24bppRgb);

            unsafe
            {
                BitmapData bmd1, bmd3;
                bmd1 = b1.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                bmd3 = br.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                int extraBytes = bmd1.Stride - width * 3;
                PHFastCpp.PHLinearConvolution((int)bmd1.Scan0, (int)bmd3.Scan0, width, height, extraBytes, mask, k, pProcessing);

                br.UnlockBits(bmd3);
                b1.UnlockBits(bmd1);

                return Operations.ConvertBitmapToStructure(br);
            }
        }

        public static ImageStructure[,] Convolution2(ImageStructure[,] image, double[,] mask, PostProcessing postProcessing, AddingType type)
        {
            int imageHeight = image.GetLength(0);
            int imageWidth = image.GetLength(1);

            int maskHeigth = mask.GetLength(0);
            int maskWidth = mask.GetLength(1);

            int maskLen = mask.GetLength(0);
            ImageStructure[,] oldImage = AddRows_Colomns(image, maskLen, type);
            ImageStructure[,] newImage = new ImageStructure[imageHeight, imageWidth];
            double resR = 0, resG = 0, resB = 0;
            double[,] tempR = new double[imageHeight, imageWidth], tempG = new double[imageHeight, imageWidth], tempB = new double[imageHeight, imageWidth];
            for (int imagei = 0; imagei < imageHeight; imagei++)
            {
                for (int imagej = 0; imagej < imageWidth; imagej++)
                {
                    for (int maski = 0; maski < maskLen; maski++)
                    {
                        for (int maskj = 0; maskj < maskLen; maskj++)
                        {
                            resR += (double)oldImage[imagei + maski, imagej + maskj].R * mask[maski, maskj];
                            resG += (double)oldImage[imagei + maski, imagej + maskj].G * mask[maski, maskj];
                            resB += (double)oldImage[imagei + maski, imagej + maskj].B * mask[maski, maskj];
                        }
                    }

                    if (postProcessing == PostProcessing.Normalization)
                    {
                        tempR[imagei, imagej] = resR;
                        tempG[imagei, imagej] = resG;
                        tempB[imagei, imagej] = resB;
                    }
                    else if (postProcessing == PostProcessing.CuttOff)
                    {
                        newImage[imagei, imagej].R = (byte)CutOff(resR, 0, 255);
                        newImage[imagei, imagej].G = (byte)CutOff(resG, 0, 255);
                        newImage[imagei, imagej].B = (byte)CutOff(resB, 0, 255);
                    }
                    else if (postProcessing == PostProcessing.None)
                    {
                        newImage[imagei, imagej].R = (byte)resR;
                        newImage[imagei, imagej].G = (byte)resG;
                        newImage[imagei, imagej].B = (byte)resB;
                    }

                    resR = resG = resB = 0;
                }
            }

            if (postProcessing == PostProcessing.Normalization)
            {
                PixelOperation.Normalization(newImage, imageHeight, imageWidth, tempR, tempG, tempB);
            }

            return newImage;
        }

        /// <summary>
        /// Adds the rows_ colomns.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="k">The k.</param>
        /// <param name="type">The type.</param>
        /// <returns> New ImageStructure with the new Rows and Colomns</returns>
        public static ImageStructure[,] AddRows_Colomns(ImageStructure[,] image, int k, AddingType type)
        {
            int newK = (k - 1) / 2;
            int imageHeight = image.GetLength(0);
            int imageWidth = image.GetLength(1);
            int newHeight = imageHeight + (newK * 2);
            int newWidth = imageWidth + (newK * 2);
            ImageStructure[,] newimage = new ImageStructure[newHeight, newWidth];

            if (type == AddingType.Black)
            {
                for (int i = 0; i < newHeight; i++)
                {
                    for (int j = 0; j < newWidth; j++)
                    {
                        if (i < imageHeight && j < imageWidth)
                        {
                            newimage[i + newK, j + newK] = image[i, j];
                        }
                    }
                }
            }
            else if (type == AddingType.Same)
            {
                for (int i = 0; i < newHeight; i++)
                {
                    for (int j = 0; j < newWidth; j++)
                    {
                        if (i <= newK && j < imageWidth && j >= newK)
                        {
                            newimage[i, j] = image[0, j];
                        }
                        else if (i > imageHeight && j < imageWidth && j >= newK)
                        {
                            newimage[i, j] = image[imageHeight - 1, j];
                        }
                        else if (j <= newK && i < imageHeight && i >= newK)
                        {
                            newimage[i, j] = image[i, 0];
                        }
                        else if (j > imageWidth && i < imageHeight && i >= newK)
                        {
                            newimage[i, j] = image[i, imageWidth - 1];
                        }

                        if (i < imageHeight && i >= newK && j < imageWidth && j >= newK)
                        {
                            newimage[i + newK, j + newK] = image[i, j];
                        }
                    }
                }
            }

            return newimage;
        }

        /// <summary>
        /// Shearpings the laplacian.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>shearping lablacin image</returns>
        public static ImageStructure[,] ShearpingLaplacianBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { -1, -1, -1 }, { -1, 9, -1 }, { -1, -1, -1 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Shearpings the laplacian same.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>>shearping lablacin image</returns>
        public static ImageStructure[,] ShearpingLaplacianSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { -1, -1, -1 }, { -1, 9, -1 }, { -1, -1, -1 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Shearpings the horizontal.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Shearpings the horizontal image</returns>
        public static ImageStructure[,] ShearpingHorizontalBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 0, 1, 0 }, { 0, 1, 0 }, { 0, -1, 0 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Shearpings the vertical.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns> ShearpingVertical image</returns>
        public static ImageStructure[,] ShearpingVerticalBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 0, 0, 0 }, { 1, 1, -1 }, { 0, 0, 0 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Shearpings the rigth diagonal.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Shearping Rigth Diagonal image</returns>
        public static ImageStructure[,] ShearpingRigthDiagonalBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 0, 0, 1 }, { 0, 1, 0 }, { -1, 0, 0 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Shearpings the left diagonal.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Shearpings the left diagonal image</returns>
        public static ImageStructure[,] ShearpingLeftDiagonalBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, -1 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Shearpings the horizontal.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Shearpings the horizontal image</returns>
        public static ImageStructure[,] ShearpingHorizontalSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 0, 1, 0 }, { 0, 1, 0 }, { 0, -1, 0 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Shearpings the vertical.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns> ShearpingVertical image</returns>
        public static ImageStructure[,] ShearpingVerticalSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 0, 0, 0 }, { 1, 1, -1 }, { 0, 0, 0 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Shearpings the rigth diagonal.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Shearping Rigth Diagonal image</returns>
        public static ImageStructure[,] ShearpingRigthDiagonalSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 0, 0, 1 }, { 0, 1, 0 }, { -1, 0, 0 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Shearpings the left diagonal.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Shearpings the left diagonal image</returns>
        public static ImageStructure[,] ShearpingLeftDiagonalSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, -1 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction laplacian.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Edge Detiction Laplacian image filters</returns>
        public static ImageStructure[,] EdgeDetictionLaplacianSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { -1, -1, -1 }, { -1, 8, -1 }, { -1, -1, -1 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction laplacian black.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Edges the detiction laplacian black images</returns>
        public static ImageStructure[,] EdgeDetictionLaplacianBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { -1, -1, -1 }, { -1, 8, -1 }, { -1, -1, -1 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction horizontal black.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns> Edges the detiction horizontal image</returns>
        public static ImageStructure[,] EdgeDetictionHorizontalBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 5, 5, 5 }, { -3, 0, -3 }, { -3, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction horizontal Same.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns> Edges the detiction horizontal image</returns>
        public static ImageStructure[,] EdgeDetictionHorizontalSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 5, 5, 5 }, { -3, 0, -3 }, { -3, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction Vertical black.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns> Edges the detiction horizontal image</returns>
        public static ImageStructure[,] EdgeDetictionVerticalBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 5, -3, -3 }, { 5, 0, -3 }, { 5, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction Vertical Same.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns> Edges the detiction horizontal image</returns>
        public static ImageStructure[,] EdgeDetictionVerticalSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 5, -3, -3 }, { 5, 0, -3 }, { 5, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction diagonal left black.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Edges the detiction diagonal left image</returns>
        public static ImageStructure[,] EdgeDetictionDiagonalLeftBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { -3, 5, 5 }, { -3, 0, 5 }, { -3, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction diagonal Left Same.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Edges the detiction diagonal left image</returns>
        public static ImageStructure[,] EdgeDetictionDiagonalLeftSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { -3, 5, 5 }, { -3, 0, 5 }, { -3, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction diagonal Rigth Black.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Edges the detiction diagonal Rigth image</returns>
        public static ImageStructure[,] EdgeDetictionDiagonalRigthBlack(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 5, 5, -3 }, { 5, 0, -3 }, { -3, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Black);
            return newimage;
        }

        /// <summary>
        /// Edges the detiction diagonal Rigth Same.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Edges the detiction diagonal Rigth image</returns>
        public static ImageStructure[,] EdgeDetictionDiagonalRigthSame(DImage image)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 5, 5, -3 }, { 5, 0, -3 }, { -3, -3, -3 } };
            newimage = NeighborhoodOperations.Convolution2(image.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Cuts the off.
        /// </summary>
        /// <param name="x">The Number.</param>
        /// <param name="min">The min.</param>
        /// <param name="max">The max.</param>
        /// <returns> New resut Cutted</returns>
        public static double CutOff(double x, double min, double max)
        {
            if (x < min)
            {
                x = min;
            }
            else if (x > max)
            {
                x = max;
            }

            return x;
        }

        /// <summary>
        /// Saves the mask byte.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="path">The path.</param>
        public static void SaveMaskByte(byte[,] mask, string path)
        {
            FileStream fT = new FileStream(path, FileMode.Create, FileAccess.Write);
            StreamWriter sW = new StreamWriter(fT);
            string arr = string.Empty;
            sW.Write(mask.GetLength(0).ToString() + " " + mask.GetLength(1).ToString() + "\n");
            try
            {
                for (int i = 0; i < mask.GetLength(0); i++)
                {
                    for (int j = 0; j < mask.GetLength(1); j++)
                    {
                        arr += mask[i, j].ToString() + "&";
                    }
                }

                arr = arr.Remove(arr.Length - 1, 1);
                sW.Write(arr);
            }
            catch (Exception e)
            {
                Logger.LogException(e);
            }
            finally
            {
                sW.Close();
                fT.Close();
            }
        }

        /// <summary>
        /// Saves the mask.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="path">The path.</param>
        public static void SaveMask(double[,] mask, string path)
        {
            FileStream fT = new FileStream(path, FileMode.Create, FileAccess.Write);
            StreamWriter sW = new StreamWriter(fT);
            string arr = string.Empty;
            sW.Write(mask.GetLength(0).ToString() + " " + mask.GetLength(1).ToString() + "\n");
            try
            {
                for (int i = 0; i < mask.GetLength(0); i++)
                {
                    for (int j = 0; j < mask.GetLength(1); j++)
                    {
                        arr += mask[i, j].ToString() + "&";
                    }
                }

                arr = arr.Remove(arr.Length - 1, 1);
                sW.Write(arr);
            }
            catch (Exception e)
            {
                Logger.LogException(e);
            }
            finally
            {
                sW.Close();
                fT.Close();
            }
        }

        /// <summary>
        /// Loads the mak byte.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns>new mask byte </returns>
        public static byte[,] LoadMakByte(string path)
        {
            FileStream fT = new FileStream(path, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(fT);
            int width, heigth;
            string length = sr.ReadLine();
            string[] widthHeigth = length.Split(' ');
            width = Convert.ToInt32(widthHeigth[1]);
            heigth = Convert.ToInt32(widthHeigth[0]);
            byte[,] newmask = new byte[heigth, width];
            try
            {
                string read = sr.ReadToEnd();
                string[] valuesStr = read.Split('&');
                int count = 0;
                for (int i = 0; i < heigth; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        newmask[i, j] = Convert.ToByte(valuesStr[count]);
                        count++;
                    }
                }
            }
            catch (Exception e)
            {
                Logger.LogException(e);
            }
            finally
            {
                sr.Close();
                fT.Close();
            }

            return newmask;
        }

        /// <summary>
        /// Loads the mak.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns>new mask</returns>
        public static double[,] LoadMak(string path)
        {
            FileStream fT = new FileStream(path, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(fT);
            int width, heigth;
            string length = sr.ReadLine();
            string[] widthHeigth = length.Split(' ');
            width = Convert.ToInt32(widthHeigth[1]);
            heigth = Convert.ToInt32(widthHeigth[0]);
            double[,] newmask = new double[heigth, width];
            try
            {
                string read = sr.ReadToEnd();
                string[] valuesStr = read.Split('&');
                int count = 0;
                for (int i = 0; i < heigth; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        newmask[i, j] = Convert.ToDouble(valuesStr[count]);
                        count++;
                    }
                }
            }
            catch (Exception e)
            {
                Logger.LogException(e);
            }
            finally
            {
                sr.Close();
                fT.Close();
            }

            return newmask;
        }

        /// <summary>
        /// Means the filter.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="maskSize">Size of the mask.</param>
        /// <returns>
        /// new image with mean filter
        /// </returns>
        public static ImageStructure[,] MeanFilter2D(DImage originalImage, int maskSize)
        {
            double[,] mask = new double[maskSize, maskSize];
            for (int i = 0; i < maskSize; i++)
            {
                for (int j = 0; j < maskSize; j++)
                {
                    mask[i, j] = Math.Pow(maskSize, 2);
                    mask[i, j] = 1 / mask[i, j];
                }
            }

            ImageStructure[,] newImage = NeighborhoodOperations.Convolution(originalImage.ImageStructArray, mask, PostProcessing.CuttOff, AddingType.Black);
            return newImage;
        }

        /// <summary>
        /// Meanfilter1s the D.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="maskSize">Size of the mask.</param>
        /// <returns>Filtered out bitmap</returns>
        public static Bitmap Meanfilter1D(Bitmap originalImage, int maskSize)
        {
            Bitmap bmp = new Bitmap(originalImage.Width, originalImage.Height, PixelFormat.Format24bppRgb);

            BitmapData bmpOriginalD = originalImage.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData bmpD = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int oldExtraBytes = bmpOriginalD.Stride - (bmp.Width * 3);
            int newExtraBytes = bmpD.Stride - (bmp.Width * 3);

            PHFastCpp.PHMean1DFilter((int)bmpOriginalD.Scan0, (int)bmpD.Scan0, bmp.Width, bmp.Height, oldExtraBytes, newExtraBytes, maskSize);

            originalImage.UnlockBits(bmpOriginalD);
            bmp.UnlockBits(bmpD);

            return bmp;
        }

        public static Bitmap GaussianFilter1D(Bitmap originalImage, double sigma)
        {
            Bitmap bmp = new Bitmap(originalImage.Width, originalImage.Height, PixelFormat.Format24bppRgb);

            BitmapData bmpOriginalD = originalImage.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData bmpD = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int oldExtraBytes = bmpOriginalD.Stride - (bmp.Width * 3);
            int newExtraBytes = bmpD.Stride - (bmp.Width * 3);

            PHFastCpp.PHGaussian1DFilter((int)bmpOriginalD.Scan0, (int)bmpD.Scan0, bmp.Width, bmp.Height, oldExtraBytes, newExtraBytes, sigma);

            originalImage.UnlockBits(bmpOriginalD);
            bmp.UnlockBits(bmpD);

            return bmp;
        }

        /// <summary>
        /// Gaussians the filter.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="segma">Size of the mask.</param>
        /// <returns>
        /// new image with Gaussian Filter
        /// </returns>
        public static ImageStructure[,] GaussianFilter(DImage originalImage, double segma)
        {
            int n = (int)((3.7 * segma) - 0.5);
            int maskSize = (2 * n) + 1;
            int halfMask = (int)Math.Floor(maskSize / 2.0);
            double[,] mask = new double[maskSize, maskSize];
            int x = 0, y = 0;
            double temp = 0;
            double sigmaSqr = Math.Pow(segma, 2);
            for (int i = -halfMask; i <= halfMask; i++)
            {
                for (int j = -halfMask; j <= halfMask; j++)
                {
                    temp = ((j * j) + (i * i));
                    mask[y, x] = 1 / (2 * Math.PI * sigmaSqr);
                    mask[y, x] *= Math.Exp(-1 * (temp / (2 * sigmaSqr)));
                    x++;
                }

                y++;
                x = 0;
            }

            ImageStructure[,] newImage = NeighborhoodOperations.Convolution(originalImage.ImageStructArray, mask, PostProcessing.None, AddingType.Same);
            return newImage;
        }

        /// <summary>
        /// Sobles the X.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <returns>new image after soblex filter</returns>
        public static ImageStructure[,] SobleX(DImage originalImage)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 1, 0, -1 }, { 1, 0, -1 }, { 1, 0, -1 } };
            newimage = NeighborhoodOperations.Convolution2(originalImage.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Same);
            return newimage;
        }

        /// <summary>
        /// Sobles the y.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <returns>new image after soblex filter</returns>
        public static ImageStructure[,] SobleY(DImage originalImage)
        {
            ImageStructure[,] newimage;
            double[,] mask = new double[,] { { 1, 1, 1 }, { 0, 0, 0 }, { -1, -1, -1 } };
            newimage = NeighborhoodOperations.Convolution2(originalImage.ImageStructArray, mask, PostProcessing.Normalization, AddingType.Same);
            return newimage;
        }
    }
}
